﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace SBS_Map_Editor.Manip
{
    //class Translation
    public sealed partial class Manipulator
    {
        private void InitTranslations()
        {
            Dictionary<AxisFlags, Matrix> axis_transforms = new Dictionary<AxisFlags, Matrix>();

            axis_transforms[AxisFlags.X] = Matrix.CreateRotationZ(-MathHelper.PiOver2);
            axis_transforms[AxisFlags.Y] = Matrix.Identity;
            axis_transforms[AxisFlags.Z] = Matrix.CreateRotationX(MathHelper.PiOver2);
            axis_transforms[AxisFlags.X | AxisFlags.Y] = Matrix.Identity;
            axis_transforms[AxisFlags.X | AxisFlags.Z] = Matrix.CreateRotationX(MathHelper.PiOver2);
            axis_transforms[AxisFlags.Y | AxisFlags.Z] = Matrix.CreateRotationY(-MathHelper.PiOver2);

            #region Draw Axis
            Action<Matrix> base_draw_axis = new Action<Matrix>(delegate(Matrix transform)
                {
                    GraphicsDevice Graphics = mGraphics.GraphicsDevice;

                    ManipulatorSettings.TranslationSettings settings = mSettings.Translation;
                });

            mDrawFunctions[TransformationMode.TranslationAxis][AxisFlags.X]
                = mDrawFunctions[TransformationMode.TranslationAxis][AxisFlags.Y]
                = mDrawFunctions[TransformationMode.TranslationAxis][AxisFlags.Z]
                    = delegate(AxisFlags axis)
                    {
                        base_draw_axis(axis_transforms[axis]);
                    };
            #endregion

            #region Draw Plane
            Action<Matrix> base_draw_plane = new Action<Matrix>(delegate(Matrix transform)
            {
                GraphicsDevice device = mGraphics.GraphicsDevice;
                ManipulatorSettings.TranslationSettings settings = mSettings.Translation;

                Vector3 up = Vector3.UnitY * settings.PlaneQuadrantSize;
                Vector3 right = Vector3.UnitX * settings.PlaneQuadrantSize;

                bool draw_pos = (settings.AxisDrawMode & AxisDirections.Positive) == AxisDirections.Positive;
                bool draw_neg = (settings.AxisDrawMode & AxisDirections.Negative) == AxisDirections.Negative;

                if (draw_pos)
                    Primitives.DrawTriangle(device, up, right, Vector3.Zero, transform);

                if (draw_neg)
                    Primitives.DrawTriangle(device, -up, -right, Vector3.Zero, transform);

                if (draw_pos && draw_neg)
                {
                    Primitives.DrawTriangle(device, -up, right, Vector3.Zero, transform);
                    Primitives.DrawTriangle(device, up, -right, Vector3.Zero, transform);
                }
            });

            mDrawFunctions[TransformationMode.TranslationPlane][AxisFlags.X | AxisFlags.Y]
                = mDrawFunctions[TransformationMode.TranslationPlane][AxisFlags.X | AxisFlags.Z]
                = mDrawFunctions[TransformationMode.TranslationPlane][AxisFlags.Y | AxisFlags.Z]
                    = delegate(AxisFlags axis)
                    {
                        base_draw_plane(axis_transforms[axis]);
                    };
            #endregion

            #region Translation Axis
            mManipFunctions[TransformationMode.TranslationAxis][AxisFlags.X]
                = mManipFunctions[TransformationMode.TranslationAxis][AxisFlags.Y]
                = mManipFunctions[TransformationMode.TranslationAxis][AxisFlags.Z]
                = delegate()
                {
                    Vector3 axis = GetUnitAxis(SelectedAxes);

                    Matrix translation = Matrix.CreateTranslation(mTransform.Translation);

                    Vector3 start_position = Viewport.Project(Vector3.Zero, ProjectionMatrix, ViewMatrix, translation);

                    Vector3 end_position = Viewport.Project(axis, ProjectionMatrix, ViewMatrix, translation);

                    Vector3 screen_direction = Vector3.Normalize(end_position - start_position);

                    end_position = start_position + (screen_direction * (Vector3.Dot(new Vector3(mInput.Delta, 0f), screen_direction)));

                    start_position = Viewport.Unproject(start_position, ProjectionMatrix, ViewMatrix, translation);
                    end_position = Viewport.Unproject(end_position, ProjectionMatrix, ViewMatrix, translation);

                    Vector3 difference = end_position - start_position;

                    BoundingFrustum frust = new BoundingFrustum(ViewMatrix * ProjectionMatrix);

                    if (frust.Contains(mTransform.Translation + difference) == ContainmentType.Contains)
                    {
                        mTransform.Translation += difference;
                    }
                };
            #endregion

            #region Translation Plane
            mManipFunctions[TransformationMode.TranslationPlane][AxisFlags.X]
                = mManipFunctions[TransformationMode.TranslationPlane][AxisFlags.Y]
                = mManipFunctions[TransformationMode.TranslationPlane][AxisFlags.Z]
                = delegate()
                {
                    Plane p = GetPlane(SelectedAxes);

                    Ray sray = GetPickRay(mInput.Start);
                    Ray eray = GetPickRay(mInput.End);

                    float? sisect = sray.Intersects(p);
                    float? eisect = eray.Intersects(p);

                    if (!sisect.HasValue || !eisect.HasValue)
                        return;

                    Vector3 spos = sray.Position + (sray.Direction * sisect.Value);
                    Vector3 epos = eray.Position + (eray.Direction * eisect.Value);

                    Vector3 diff = epos - spos;

                    BoundingFrustum frust = new BoundingFrustum(ViewMatrix * ProjectionMatrix);

                    if (frust.Contains(mTransform.Translation + diff) == ContainmentType.Contains)
                    {
                        mTransform.Translation += diff;
                    }
                };
            #endregion
        }
    }
}